#include "GP_handler.h"
#include "thread.h"
#include "vmm.h"
#include "keyboard.h"
#include "print.h"
#include "x86_disassemble.h"
#include "global.h"
#include "io.h"
#include "shadow_page_table.h"
#include "scan_patch.h"

uint8_t guest_cursor_high_state = 0x0;
uint8_t guest_cursor_low_state = 0x0;

int virtual_ide_read_state = 0x0;
int virtual_ide_write_state = 0x0;
uint16_t virtual_lba = 0x0;
uint16_t counter = 0x0;
bool is_susceptible = false;
int susceptible_i = 0x0;
struct x86_instruction_t* x86_struct;
uint32_t eflags__;
int mod_, sreg_ ,reg_;

uint32_t last_cursor = NULL;

struct x86_instruction_t GP_x86_struct;

/* 通用保护异常处理函数 异常中断号0x0d */
void GP_handler(uint8_t vec_nr) {
   struct task_struct* cur_thread = running_thread();
   struct vcpu* vm_thread_cpu = (struct vcpu*)(cur_thread->guest_ptr);
   struct intr_stack* intr_stack;
   //uint32_t* esp;

   //asm ("mov %%esp, %0" : "=g" (esp));   
   //int function_stack_size = (*((int*)(0xc0000b0f)) - (((int)(esp)) + sizeof(struct intr_stack)));
   intr_stack = (struct intr_stack*)((*((uint32_t*)(0xc0000b0f))) - sizeof(struct intr_stack));

   put_state = false;
   GP_x86_struct.i_addr = intr_stack->eip;
   x86_dis(&GP_x86_struct);
   put_state = true;
   
   switch(*((unsigned char*)(intr_stack->eip))) {
      case CLI: //cli
      is_susceptible = false;
      susceptible_i = 0x0;
      for (;susceptible_i <  susceptible_count;susceptible_i++) {
         if ((uint32_t)intr_stack->eip == ((uint32_t)susceptible_i_tables[susceptible_i]->start_i & 0x0000ffff)) {
            x86_struct = susceptible_i_tables[susceptible_i];
            is_susceptible = true;
            break;
         }
      }
      if (is_susceptible) {
         switch(x86_struct->opcode[0]) {
            case PUSHF_:
            eflags__ = intr_stack->eflags;
            eflags__ |= (1 << 9);
            intr_stack->esp -= 4;
            *((uint32_t*)intr_stack->esp) = eflags__;
            intr_stack->eip += 1;
            break;

            case IRET_: //iret
            // *((uint32_t*)(&(((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->eflags))) |= (1 << 9);
            if (vm_thread_cpu->CPL_ == ((*((uint32_t*)(((uint32_t)intr_stack->esp) + (uint32_t)4))) & 0x3)) {
               vm_thread_cpu->cs = *((uint32_t*)((uint32_t)intr_stack->esp + (uint32_t)0x4));
               vm_thread_cpu->eflags = *((uint32_t*)((uint32_t)intr_stack->esp + (uint32_t)0x8));

               intr_stack->eip = (void*)(*((uint32_t*)intr_stack->esp));
               intr_stack->cs = 0x2b;
               intr_stack->esp = (void*)(((uint32_t)intr_stack->esp) + ((uint32_t)12));
               intr_stack->ss = 0x33;

               vm_thread_cpu->CPL_ = 0x0;
               
               x86_scan((void *)intr_stack->eip);
               
            } else {
               put_str("\niret trap error");
            }
            break;

            case MOV_RM16_SREG:
            mod_ = (x86_struct->modrm >> 6) & 0x03;
            sreg_ = (x86_struct->modrm >> 3) & 0x07;
            reg_ = x86_struct->modrm & 0x07;
            switch (mod_) {
               case 3:
               intr_stack->ebx = *((uint16_t*)((uint32_t)vm_thread_cpu + (uint32_t)((8 + sreg_) * 4)));//test
               break;
               default:
               asm("jmp .");
            }
            if (x86_struct->prefix_count == 1) {
               intr_stack->eip += 3;
            } else {
               intr_stack->eip += 2;
            }
            break;

            case MOV_SREG_RM16:
            mod_ = (x86_struct->modrm >> 6) & 0x03;
            sreg_ = (x86_struct->modrm >> 3) & 0x07;
            reg_ = x86_struct->modrm & 0x07;
            switch (mod_) {
               case 3:
               *((uint32_t*)((uint32_t)vm_thread_cpu + (uint32_t)((8 + sreg_) * 4))) = intr_stack->eax;
               break;
               default:
               asm("jmp .");
            }
            if (x86_struct->prefix_count == 1) {
               intr_stack->eip += 3;
            } else {
               intr_stack->eip += 2;
            }
            break;
            default:
            asm("jmp .");
         }
      } else {
         *((uint32_t*)(&(((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->eflags))) &= ~(1 << 9);
         intr_stack->eip += 1;
      }
      break;
      case STI: //sti
      *((uint32_t*)(&(((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->eflags))) |= (1 << 9);
      intr_stack->eip += 1;
      put_str("vm sti\n");
      //put_str("sti\n");
      
      // test_patch_entry = Restore_Patch_Value((uint32_t)(intr_stack->eip));
      
      // if (test_patch_entry->patch_addr != 0) {
      //    if ((uint32_t)intr_stack->eip == 0x0) {
      //       gp_state = true;
      //    }
      //    if (gp_state) {
      //       put_int((uint32_t)(intr_stack->eip));
      //       put_str("\n");
      //       put_int((uint32_t)(test_patch_entry->patch_addr));
      //       put_str("\n");
      //       asm("jmp .");
      //    }
         
      //    if (test_patch_entry->Value_before_patching == RET) {
      //       // put_int(intr_stack->eip);
      //       // put_str("\n");
      //       intr_stack->eip = (void*)(*((uint32_t*)(intr_stack->esp)));
      //       intr_stack->esp += 4;
      //       // put_int(intr_stack->eip);
      //       // put_str("\n");
      //       //asm("jmp .");
      //       x86_scan((void *)intr_stack->eip);
      //    } else if (test_patch_entry->Value_before_patching == RET_IMM16) {
      //       intr_stack->eip = (void*)(*((uint32_t*)(intr_stack->esp)));
      //       intr_stack->esp += 8;
      //       x86_scan((void *)intr_stack->eip);
      //    } else if (test_patch_entry->Value_before_patching == CALL_RM32) {
      //       scan_patch_mod = *((uint32_t*)(intr_stack->eip + 0x1));
      //       switch ((scan_patch_mod >> 3) & 0x7) {
      //          case 0x2:
      //          switch ((scan_patch_mod >> 6) & 0x3) {
      //             case 0x3://0xd0
      //             intr_stack->esp -= 4;
      //             *((uint32_t*)(intr_stack->esp)) = intr_stack->eip + 0x2;
      //             intr_stack->eip = (void*)(intr_stack->eax);
      //             break;
      //             case 0x00:
      //             intr_stack->esp -= 4;
      //             *((uint32_t*)(intr_stack->esp)) = intr_stack->eip + 0x6;
      //             intr_stack->eip = (void*)*((uint32_t*)(*((uint32_t*)(intr_stack->eip + 0x2))));
      //             // put_int(intr_stack->eip);
      //             // put_str("\n");
      //             // asm("jmp .");
      //             break;
      //          }
      //          break;
      //       }
      //       x86_scan((void *)intr_stack->eip);
      //    }
      //    else {
      //       *((uint8_t*)(test_patch_entry->patch_addr)) = test_patch_entry->Value_before_patching;
      //       x86_scan((void *)test_patch_entry->patch_addr);
      //       if (test_patch_entry->Value_before_patching == STI) {
      //          test_patch_entry->patch_addr = 0x0;
      //       }
      //       // if (intr_stack->eip == 0x1a9c) {
      //       //    put_str(" 0x1a9c \n");
      //       //    asm("jmp .");
      //       // }
      //    }
      // } else {
      //    *((uint32_t*)(&(((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->eflags))) |= (1 << 9);
      //    intr_stack->eip += 1;
      //    put_str("vm sti\n");
      // }
      break;

      case 0xe6: // out eoi
      ((struct PIC8259A*)((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->pic)->isr = 0x0;
      intr_stack->eip += 2;
      break;

      case 0xec:
      intr_stack->eip += 1;
      switch ((uint16_t)(intr_stack->edx)) {
         case 0x60:
         *((uint8_t*)&(intr_stack->eax)) = (uint8_t)(guest_kbd_buf.keyboard_buf[guest_kbd_buf.tail]);
         if (guest_kbd_buf.tail == 100) {
            guest_kbd_buf.tail = 0x0;
         } else {
            guest_kbd_buf.tail++;
         }
         //asm("jmp .");
         break;
         case 0x03d5:
         switch ((uint8_t)(intr_stack->eax)) {
            case 0x0e:
            intr_stack->eax = (intr_stack->eax & 0xFFFFFF00) | (uint8_t)(vm_thread_cpu->h_cursor);
            break;
            case 0x0f:
            intr_stack->eax = (intr_stack->eax & 0xFFFFFF00) | (uint8_t)(vm_thread_cpu->l_cursor); 
            break;
         }
         break;

      }
      // if (((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x60))) { //inb 0x60, al
         
      // }
      // else if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x03d5))) && (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x0e)))) {
      //    intr_stack->eax = (uint8_t)((guest_kbd_buf.guest_cursor >> 8 ) & 0xff); 
      // }
      // else if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x03d5))) && (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x0f)))) {
      //    intr_stack->eax = (uint8_t)((guest_kbd_buf.guest_cursor) & 0xff); 
      // }
      // else if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x1f7)))) {
      //    //virtual_ide_thread_state = 0x1;
      // }
      break;
      case 0x0f:
      if ((*((unsigned char*)(intr_stack->eip + 1)) == 0x22) && (*((unsigned char*)(intr_stack->eip + 2)) == 0xd8)) {
         ((struct vcpu*)((struct task_struct*)cur_thread)->guest_ptr)->cr3 = intr_stack->eax;
         uint32_t shadow_pt = NULL;
         if (vm_thread_cpu->shadow_pt_map_) {
            shadow_pt = (uint32_t)find_shadow_page_table(vm_thread_cpu);
         }
         if (shadow_pt) {
            asm volatile ("movl %0, %%cr3" : : "r" (addr_v2p(shadow_pt)) : "memory");   //更新页目录寄存器cr3,使新页表生效
         } else {
            shadow_pt = (uint32_t)create_shadow_page_table(vm_thread_cpu);
            if (shadow_pt) {
               asm volatile ("movl %0, %%cr3" : : "r" (addr_v2p(shadow_pt)) : "memory");   //更新页目录寄存器cr3,使新页表生效
            } else {
               put_str("\nvmm shadow page table error");
               asm("jmp .");
            }
         }

         intr_stack->eip += 3; 
      }
      else if ((*((unsigned char*)(intr_stack->eip + 1)) == 0x20) && (*((unsigned char*)(intr_stack->eip + 2)) == 0xc0)) {
         intr_stack->eip += 3;
      } 
      else if ((*((unsigned char*)(intr_stack->eip + 1)) == 0x22) && (*((unsigned char*)(intr_stack->eip + 2)) == 0xc0)) {
         intr_stack->eip += 3;
      } 
      // else if ((*((unsigned char*)(intr_stack->eip + 1)) == 0x01)) {
      //    ((struct vcpu*)(cur_thread->guest_ptr))->idtr_base = *((uint32_t*)((0x0 + intr_stack->ebp) - 0xe));
         
      //    intr_stack->eip += 4;
      // }
      switch (GP_x86_struct.opcode[1]) {
         case 0x00:
         switch ((GP_x86_struct.modrm >> 3) & 0x07) {
            case 0x3:
            vm_thread_cpu->tr = (uint16_t)(intr_stack->eax & 0xffff);
            put_int(intr_stack->eax);
            break;
         }
         intr_stack->eip += 3;
         break;

         case 0x01:
         switch ((GP_x86_struct.modrm >> 3) & 0x07) {
            case 0x2:
            switch (GP_x86_struct.modrm & 0x07) {
               case 0x5:
               if (GP_x86_struct.displacement_size) {
                  if (GP_x86_struct.displacement & 0x80) {
                     uint8_t displacement = ~((uint8_t)GP_x86_struct.displacement);
                     vm_thread_cpu->gdtr_base = *((uint32_t*)((uint32_t)intr_stack->ebp - ((uint32_t)displacement - (uint32_t)1)));
                     put_int(vm_thread_cpu->gdtr_base);
                  } else {
                     asm("jmp .");
                  }
               }
               break;
            }
            break;
            case 0x3:
            switch (GP_x86_struct.modrm & 0x07) {
               case 0x5:
               if (GP_x86_struct.displacement_size) {
                  if (GP_x86_struct.displacement & 0x80) {
                     uint8_t displacement = ~((uint8_t)GP_x86_struct.displacement);
                     vm_thread_cpu->idtr_base = *((uint32_t*)((uint32_t)intr_stack->ebp - ((uint32_t)displacement - (uint32_t)1)));
                     // put_int(displacement);
                     put_int(vm_thread_cpu->idtr_base);

                  } else {
                     asm("jmp .");
                  }
               }
               break;
            }
            break;
         }
         intr_stack->eip += 4;
         break;
      }
      // else {
      //    asm("jmp .");
      //    intr_stack->eip += 4;
      // }
      break;
      case 0xee:
      intr_stack->eip += 1;
      
      if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x03d4))) && (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x0e)))) {
         guest_cursor_high_state = 0x1;
         if (last_cursor) {
            *((uint8_t*)(0xb8000 + (last_cursor * 2))) = 0x00;
            *((uint8_t*)(0xb8000 + (last_cursor * 2) + 0x1)) = 0x00;
            last_cursor = NULL;
         }
      }
      else if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x03d4))) && (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x0f)))) {
         guest_cursor_low_state = 0x1;
      }
      else if (((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x03d5))) {
         if (guest_cursor_high_state == 0x1) {
            vm_thread_cpu->h_cursor = (uint8_t)intr_stack->eax;
            guest_cursor_high_state = 0x0;
            // if (guest_kbd_buf.guest_keyboard_state) {
            //    uint16_t result = ((uint8_t)(vm_thread_cpu->h_cursor & 0xFF) << 8) | (uint8_t)(vm_thread_cpu->l_cursor & 0xFF);
            //    *((uint8_t*)(0xb8000 + (result * 2))) = 
            //    // //asm("jmp .");
            //    // outb((uint16_t)0x3d4, (uint8_t)0xe);
            //    // outb((uint16_t)0x3d5, (result >> 8 ) & 0xff);
            // }
            // outb((uint16_t)0x3d4, (uint8_t)0xe);
            // outb((uint16_t)0x3d5, (guest_kbd_buf.guest_cursor >> 8 ) & 0xff);
            
         }
         else if (guest_cursor_low_state == 0x1) {
            vm_thread_cpu->l_cursor = (uint8_t)intr_stack->eax;
            guest_cursor_low_state = 0x0;
            uint16_t result = ((uint8_t)(vm_thread_cpu->h_cursor & 0xFF) << 8) | (uint8_t)(vm_thread_cpu->l_cursor & 0xFF);
            // if (last_cursor) {
            //    *((uint8_t*)(0xb8000 + (last_cursor * 2))) = 0x00;
            //    *((uint8_t*)(0xb8000 + (last_cursor * 2) + 0x1)) = 0x00;
            // }
            *((uint8_t*)(0xb8000 + (result * 2))) = '_';
            *((uint8_t*)(0xb8000 + (result * 2) + 0x1)) = 0x8f;
            last_cursor = result;
            // if (guest_kbd_buf.guest_keyboard_state) {
               

            //    // outb((uint16_t)0x3d4, (uint8_t)0xf);
            //    // outb((uint16_t)0x3d5, (result & 0xff));
            // } else {
            //    *((uint8_t*)(0xb8000 + (result * 2))) = 0x00;
            //    *((uint8_t*)(0xb8000 + (result * 2) + 0x1)) = 0x00;
            // }
            // outb((uint16_t)0x3d4, (uint8_t)0xf);
            // outb((uint16_t)0x3d5, (guest_kbd_buf.guest_cursor & 0xff));
         }
      } 
      else if ((((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x1f7)))) {
         if (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x20))) {
            //asm("jmp .");
            virtual_ide_read_state = 0x1;
         }
         else if (((uint8_t)(intr_stack->eax)) == ((uint8_t)(0x30))) {
            //asm("jmp .");
            virtual_ide_write_state = 0x1;
         }
      }
      else if (((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x01f3))) {
         virtual_lba = ((uint8_t)(intr_stack->eax));
      }
      else if (((uint16_t)(intr_stack->edx)) == ((uint16_t)(0x01f4))) {
         virtual_lba += ((((uint8_t)(intr_stack->eax))) <<  8);
      }
      break;
      case 0x66:
      if (*((unsigned char*)(intr_stack->eip + 2)) == 0x6d) {
         *((uint16_t*)(intr_stack->edi)) = *((uint16_t*)(0xc0055000 + (counter)));
         if (intr_stack->ecx == 0x0) {
            intr_stack->eip += 3;
            counter = 0x0;
            memset((void*)(0xc0055000), 0x0, 512);
         } else {
            intr_stack->ecx--;
            intr_stack->edi += 2;
            counter += 2;
         }
      } 
      else if (*((unsigned char*)(intr_stack->eip + 2)) == 0x6f) {
         *((uint16_t*)(0xc0055000 + (counter))) = *((uint16_t*)(intr_stack->esi));
         if (intr_stack->ecx == 0x0) {
            intr_stack->eip += 3;
            counter = 0x0;
            
         } else {
            intr_stack->ecx--;
            intr_stack->esi += 2;
            counter += 2;
         }
      }
    
      break;

      default:
      put_str("VMM GP");
      asm("jmp .");      

   }

}


